#import <appkit/OpenPanel.h>
#import <appkit/publicWraps.h>
#import <appkit/Application.h>
#import <appkit/NXImage.h>
#import <appkit/PrintInfo.h>
#import <appkit/Pasteboard.h>
#import <stdio.h>
#import <stdlib.h>
#import <string.h>
#import <sys/types.h>
#import <sys/stat.h>
#import <sys/file.h>
#import "TVController.h"
#import "PrefControl.h"
#import "ToyWin.h"
#import "ToyWinPPM.h"
#import "ToyWinEPS.h"
#import "ToyWinBMP.h"
#import "ToyWinGIF.h"
#import "ToyWinPCD.h"
#import "DirList.h"
#import "ADController.h"
#import "strfunc.h"
#import "Exttab.h"
#import "common.h"

@implementation TVController

static int winCounter = 5; /* because small window is under the menu */

#define  typeNumber	16

/* ToyViewer treats these extensions as pre-defined */
static const char *const fileType[typeNumber + 1] = {
	"tiff", "tif", "TIFF", "TIF",
	"eps", "EPS",
	"gif", "GIF",
	"bmp", "BMP",
	"ppm", "pgm", "pbm", "pnm",
	"pcd", "PCD",
	NULL };
static const char fileTypeID[typeNumber] = {
	Type_tiff, Type_tiff, Type_tiff, Type_tiff,
	Type_eps, Type_eps,
	Type_gif, Type_gif,
	Type_bmp, Type_bmp,
	Type_ppm, Type_ppm, Type_ppm, Type_ppm,
	Type_pcd, Type_pcd };
static const char **fileTypeBuffer;
static short *fileTypeIDBuffer;
static char odir[MAXFILENAMELEN];	/* Last Opened Directory */
static Exttab *extTable;


/* If file has no extension, this func. recognize its file type */
static int discriminate(const char *fn)
{
	int maybe = Type_none;
	int cc;
	FILE *fp;

	if ((fp = fopen(fn, "r")) == NULL)
		return Type_none;
	switch (cc = getc(fp)) {
	case 0x0a: maybe = Type_pcx;  break;
	case '%':  maybe = Type_eps;  break;
	case 'B':  maybe = Type_bmp;  break;
	case 'G':  maybe = Type_gif;  break;
	case 'I':  maybe = Type_tiff;  break;
	case 'M':  maybe = Type_mag;  break;
	case 'P':  maybe = Type_ppm;  break;
	case 0xff:
		if (getc(fp) == 0xd8)
			maybe = Type_jpg;
		break;
	default: break;
	}
	fclose(fp);
	return maybe;
}


- appDidInit:sender
{
	int i, j, n;
	char buf[256], *p, **q;
	NXCoord	lmg, rmg, tmg, bmg;
	const char *const *types = [NXImage imageFileTypes];

	[preference init];

	extTable = [[Exttab alloc] init];
	n = dircopy(buf, NXArgv[0], YES);
	strcpy(&buf[n], toyviewerTAB);
	[extTable readExtData: buf];
	p = getenv("HOME");
	sprintf(buf, "%s/%s", p, toyviewerRC);
	[extTable readExtData: buf];
	sprintf(buf, "%s/Library/ToyViewer/%s", p, toyviewerRC);
	[extTable readExtData: buf];
	n = [extTable entry];

	for (i = 0; types[i]; i++) ;
	n += i + typeNumber + 1;
	fileTypeBuffer = (const char **)malloc(sizeof(char *const *) * n);
	fileTypeIDBuffer = (short *)malloc(sizeof(short) * n);
	i = 0;
	if ((q = [extTable table]) != NULL)
		for (j = 0; q[j]; j++) {
			fileTypeBuffer[i] = q[j];
			fileTypeIDBuffer[i++] = Type_user;
		}
	for (j = 0; fileType[j]; j++) {
		fileTypeBuffer[i] = fileType[j];
		fileTypeIDBuffer[i++] = fileTypeID[j];
	}
	for (j = 0; types[j]; j++) {
		fileTypeBuffer[i] = types[j];
		fileTypeIDBuffer[i++] = Type_other;
	}
	fileTypeBuffer[i] = NULL;
	[DirList setExtList: fileTypeBuffer];

	printInfo = [NXApp printInfo];
	[printInfo setOrientation:NX_LANDSCAPE andAdjust:YES];
	[printInfo getMarginLeft:&lmg right:&rmg top:&tmg bottom:&bmg];
	lmg = (lmg + rmg) * 0.3;
	tmg = (tmg + bmg) * 0.3;
	[printInfo setMarginLeft:lmg right:lmg top:tmg bottom:tmg];
	return self;
}


- drawFile: (const char *)fn :(const char *)aType
	/* Return Value:  nil: Error,  id: New ToyWin */
{
	id twtmp = nil;
	int i, err;
	int itype = Type_none;
	const char *key = NULL;

	// if ([self isOpened: fn]) return self;
	if (aType && *aType) {
		for (i = 0; fileTypeBuffer[i]; i++)
			if (strcmp(aType, fileTypeBuffer[i]) == 0) {
				itype =  fileTypeIDBuffer[i];
				key = aType;
				break;
			}
	}
	if (itype == Type_none) { /* Unknown Extension */
		if ((itype = discriminate(fn)) == Type_none)
			return nil;
	}

	if (viaPipe(itype)) { /* Type_user and ... */
		if (key == NULL)
			switch (itype) {
			case Type_pcx:	key = "pcx"; break;
			case Type_mag:	key = "mag"; break;
			case Type_jpg:	key = "jpg"; break;
			}
		twtmp = [[ToyWinPPM alloc] init: self];
		[twtmp setExecList:
			[extTable execListAlloc: key with: fn] ext: key]; 
	}else if (itype == Type_pcd) {
		twtmp = [[ToyWinPCD alloc] init: self];
		[twtmp setPreference: preference];
	}else {
		switch (itype) {
		case Type_bmp:
			twtmp = [ToyWinBMP alloc]; break;
		case Type_gif:
			twtmp = [ToyWinGIF alloc]; break;
		case Type_eps:
			twtmp = [ToyWinEPS alloc]; break;
		case Type_ppm:
			twtmp = [ToyWinPPM alloc]; break;
		case Type_tiff:
		case Type_other:
		default:
			twtmp = [ToyWin alloc]; break;
		}
		[twtmp init: self];
	}

	err = [twtmp drawToyWin:(const char *)fn Type:itype Num:winCounter];
	if (err == 0) {
		[self newWindow: twtmp];
		winCounter++;
	}else {
		if (err > 0)
			errAlert(fn, err);
		[twtmp free];
		return nil;
	}
	return twtmp;	// return New ToyWin
}

- openFile:sender
{
	char fn[MAXFILENAMELEN];
	id openPanel;
    
	if (!odir[0])
		strcpy(odir, getenv("HOME"));
	openPanel = [OpenPanel new];
	[[openPanel chooseDirectories: NO] allowMultipleFiles:YES];
	if ([openPanel runModalForDirectory:odir
			file:NULL types:fileTypeBuffer]) {
		const char *const *files = [openPanel filenames];
		strcpy(odir, [openPanel directory]);
		for ( ; files && *files; files++) {
			sprintf(fn, "%s/%s", odir, *files);
			if (! [self isOpened: fn]) {
				int j;
				if ((j = getExtension(fn)) == 0)
					return self; /* No Extension */
				[self drawFile: fn : &fn[j]];
			}
		}
	}
	return self;
}

#define  AutoThreshold	4

/* Local Method */
- openDirectory: (const char *)dir
{
	char fn[MAXFILENAMELEN];
	const char *p;
	int	n, i;
	id	dirlist;

	dirlist = [[DirList alloc] init];
	n = [dirlist getDirList: dir];
	if (n <= 0) {
		errAlert(dir, Err_NOFILE);
		[dirlist free];
	}else if (n < AutoThreshold) {
		for (i = 0; i < n; i++) {
			p = [dirlist filenameAt: i];
			sprintf(fn, "%s/%s", dir, p);
			if (! [self isOpened: fn]) {
				int j = getExtension(p);
				(void)[self drawFile: fn : &p[j]];
			}
		}
		[dirlist free];
	}else { /* Auto Display */
		ADController *ad = [ADController alloc];
		[ad init:self dir:dir with:dirlist];
	}

	return self;
}

- autoDisplay:sender
{
	char fn[MAXFILENAMELEN];
	id openPanel;
	const char *const *files;

	if (!odir[0])
		strcpy(odir, getenv("HOME"));
	openPanel = [OpenPanel new];
	[[openPanel chooseDirectories: YES] allowMultipleFiles:NO];
	if (![openPanel runModalForDirectory:odir file:NULL])
		return self;

	files = [openPanel filenames];
	strcpy(odir, [openPanel directory]);
	sprintf(fn, "%s/%s", odir, *files);
	[self openDirectory: fn];
	return self;
}

- openPasteBoard:sender
{
	Pasteboard	*pb;
	const NXAtom	*cont;
	NXStream	*st;
	id   twtmp = nil;
	char *ext, fn[256];
	int  err;
	static int untitledCount = 0;

	pb = [Pasteboard new];  // don't free it
	ext = NULL;
	for (cont = [pb types]; cont && *cont; cont++) {
		if (*cont == NXTIFFPboardType) {
			twtmp = [[ToyWin alloc] init: self];
			ext = "tiff";
			break;
		}
		if (*cont == NXPostScriptPboardType) {
			twtmp = [[ToyWinEPS alloc] init: self];
			ext = "eps";
			break;
		}
	}
	if (ext == NULL) {
		NXBeep();
		return self;
	}
	st = [pb readTypeToStream: *cont];
	sprintf(fn, "%s/Untitled%d.%s", getenv("HOME"), ++untitledCount, ext);

	err = [twtmp drawFromFile:(const char *)fn or:st Num:winCounter];
	if (err == 0) {
		[self newWindow: twtmp];
		winCounter++;
	}else {
		if (err > 0)
			errAlert(fn, err);
		[twtmp free];
	}
	NXCloseMemory(st, NX_FREEBUFFER);
	return self;
}


- (int)app:sender openFile:(const char *)fn type:(const char *)aType
{
	struct stat  buf;
	int	mode;
	id	res = nil;

	if (printInfo == nil)
		[self appDidInit:sender];
	if (stat((char *)fn, &buf) != 0) return NO;
	mode = (buf.st_mode & S_IFMT);
	if (mode == S_IFDIR) {
		if (access(fn, X_OK) != 0) return NO;
		res = [self openDirectory: fn];
	}else {
		if ([self isOpened: fn]) return YES;
		res = [self drawFile: fn : aType];
	}
	return (res != nil);
}

- (BOOL)appAcceptsAnotherFile:sender
{
	return YES;
}

- preference
{
	return preference;
}

@end
